package com.example.verticles;

import com.github.rjeschke.txtmark.Processor;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Promise;
import io.vertx.core.eventbus.DeliveryOptions;
import io.vertx.core.http.HttpServer;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.BodyHandler;
import io.vertx.ext.web.templ.freemarker.FreeMarkerTemplateEngine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.LocalDateTime;

public class HttpServerVerticle extends AbstractVerticle {

    private static final Logger LOGGER = LoggerFactory.getLogger(HttpServerVerticle.class);

    public static final String CONFIG_HTTP_SERVER_PORT = "http.server.port";  // <1>
    public static final String CONFIG_WIKIDB_QUEUE = "wikidb.queue";

    private String wikiDbQueue = CONFIG_WIKIDB_QUEUE;

    private FreeMarkerTemplateEngine templateEngine;

    @Override
    public void start(Promise<Void> promise) throws Exception {
        wikiDbQueue=config().getString(CONFIG_WIKIDB_QUEUE,CONFIG_WIKIDB_QUEUE);

        HttpServer server = vertx.createHttpServer();

        Router router = Router.router(vertx);
        router.post().handler(BodyHandler.create());

        router.get("/").handler(this::indexHandler);
        router.get("/wiki/:page").handler(this::pageRenderingHandler);
        router.post("/save").handler(this::pageUpdateHandler);
        router.post("/create").handler(this::pageCreateHandler);
        router.post("/delete").handler(this::pageDeletionHandler);

        templateEngine=FreeMarkerTemplateEngine.create(vertx);

        int port=config().getInteger(CONFIG_HTTP_SERVER_PORT,8080);

        server.requestHandler(router)
                .listen(port,ar->{
                    if (ar.failed()){
                        LOGGER.error("Could not start a HTTP server",ar.cause());
                        promise.fail(ar.cause());
                    }else {
                        LOGGER.info("HTTP server running on port {}" ,port);
                        promise.complete();
                    }
                });

    }

    private void indexHandler(RoutingContext routingContext) {
        LOGGER.info("visit the home page");
        DeliveryOptions options=new DeliveryOptions().addHeader("action","all-pages");

        vertx.eventBus().request(wikiDbQueue,new JsonObject(),options,reply->{
            if (reply.failed()){
                routingContext.fail(reply.cause());
            }else {
                JsonObject body = (JsonObject) reply.result().body();
                routingContext.put("title","Wiki home");
                routingContext.put("pages",body.getJsonArray("pages").getList());
                templateEngine.render(routingContext.data(),"templates/index.ftl",ar->{
                    if (ar.failed()){
                        routingContext.fail(ar.cause());
                    }else {
                        routingContext.response().putHeader("Content-Type", "text/html");
                        routingContext.response().end(ar.result());
                    }
                });
            }
        });
    }

    private static final String EMPTY_PAGE_MARKDOWN =
            "# A new page\n" +
                    "\n" +
                    "Feel-free to write in Markdown!\n";

    private void pageRenderingHandler(RoutingContext routingContext) {
        String pageRequest = routingContext.request().getParam("page");
        JsonObject page = new JsonObject().put("page", pageRequest);
        DeliveryOptions options=new DeliveryOptions().addHeader("action","get-page");

        vertx.eventBus().request(wikiDbQueue,page,options,reply->{
            if (reply.failed()){
                routingContext.fail(reply.cause());
            }else {
                JsonObject body = (JsonObject) reply.result().body();
                Boolean found = body.getBoolean("found");
                String rawContent = body.getString("rawContent", EMPTY_PAGE_MARKDOWN);

                routingContext.put("title", pageRequest);
                routingContext.put("id", body.getInteger("id", -1));
                routingContext.put("newPage", found ? "no" : "yes");
                routingContext.put("rawContent", rawContent);
                routingContext.put("content", Processor.process(rawContent));
                routingContext.put("timestamp", LocalDateTime.now().toString());

                templateEngine.render(routingContext.data(),"templates/page.ftl",ar->{
                    if (ar.failed()){
                        routingContext.fail(ar.cause());
                    }else {
                        routingContext.response().putHeader("Content-Type", "text/html");
                        routingContext.response().end(ar.result());
                    }
                });
            }
        });
    }

    private void pageUpdateHandler(RoutingContext routingContext) {
        HttpServerRequest request = routingContext.request();
        String title = request.getParam("title");
        JsonObject requestJson = new JsonObject()
                .put("id", request.getParam("id"))
                .put("title", title)
                .put("markdown", request.getParam("markdown"));

        DeliveryOptions options=new DeliveryOptions();
        if ("yes".equals(request.getParam("newPage"))){
            options.addHeader("action","create-page");
        }else {
            options.addHeader("action","save-page");
        }

        vertx.eventBus().request(wikiDbQueue,requestJson,options,reply->{
            if (reply.failed()){
                routingContext.fail(reply.cause());
            }else {
                routingContext.response().setStatusCode(303);
                routingContext.response().putHeader("location","/wiki/"+title);
                routingContext.response().end();
            }
        });
    }

    private void pageCreateHandler(RoutingContext routingContext) {
        String pageName = routingContext.request().getParam("name");
        String location = "/wiki/" + pageName;
        if (pageName == null || pageName.isEmpty()) {
            location = "/";
        }
        routingContext.response().setStatusCode(303);
        routingContext.response().putHeader("location",location);
        routingContext.response().end();
    }

    private void pageDeletionHandler(RoutingContext routingContext) {
        String id = routingContext.request().getParam("id");
        JsonObject request = new JsonObject().put("id", id);
        DeliveryOptions options=new DeliveryOptions().addHeader("action","delete-page");

        vertx.eventBus().request(wikiDbQueue,request,options,reply->{
            if (reply.failed()){
                routingContext.fail(reply.cause());
            }else {
                routingContext.response().setStatusCode(303);
                routingContext.response().putHeader("location","/");
                routingContext.response().end();
            }
        });
    }

}
